home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / music / c2snd201.zip / PPWAV.ASM < prev    next >
Assembly Source File  |  1994-06-11  |  29KB  |  1,497 lines

  1. ; PPWAV.ASM
  2. ;
  3. ; PreProcess Wav:  this program performs one or more of the following
  4. ; conversions on a .wav file:  mix to mono, convert to 8-bit, and halve
  5. ; sampling rate.  This program is provided for users of PLAYWAV who have
  6. ; been confonted with the "Output underflow" message.  Its purpose is to
  7. ; reduce the size of a .wav file so that it can be played on limited
  8. ; hardware (such as a floppy-only system, or to a lesser extent the SL).
  9. ;     Note that mixing to mono and converting to 8-bit with PPWAV do not
  10. ; affect the sound quality when the .wav is to be played on a Tandy with 
  11. ; PLAYWAV, since those conversions must be done when the .wav is played 
  12. ; anyway.
  13. ;     Some unusual .wav types that PLAYWAV can't play can also be con-
  14. ; verted with this program (more than 16 bits per channel, more than 2
  15. ; channels, sampling rate higher than 65535 Hz).
  16. ;     The input file is copied rather then overwritten.
  17. ;     This program is in .exe format due to memory requirements.
  18. ;     Syntax is as follows:
  19. ;
  20. ;     PPWAV <input file> <output file>
  21. ;
  22. ; There are no options.  The user will be prompted for input from the key-
  23. ; board.
  24. ;
  25. ; Order of segments:
  26. ;
  27.  
  28. SCODE    SEGMENT
  29. SCODE    ENDS
  30. SDATA    SEGMENT
  31. SDATA    ENDS
  32.     ;
  33.     ; Stack segment.
  34.     ;
  35. STACK    SEGMENT    STACK
  36.     DW    512 DUP (?)
  37. STACK    ENDS
  38.     ;
  39.     ; Buffer for output samples (16K).
  40.     ;
  41. OUTBUF    SEGMENT
  42.     DB    16384 DUP (?)
  43. OUTBUF    ENDS
  44.     ;
  45.     ; Buffer for input samples (32K).
  46.     ;
  47. INBUF    SEGMENT
  48.     DB    32768 DUP (?)
  49. INBUF    ENDS
  50.  
  51. SDATA        SEGMENT
  52. ;
  53. ; Data.
  54. ;
  55. ; Template for .wav header.  This header will be filled in to match the
  56. ; output file and written out.  The strings below are also used to verify
  57. ; the input .wav.
  58. ;
  59. WAVHEADER    EQU    $
  60. RIFFSTR        DB    "RIFF"        ; RIFF signature = "RIFF"
  61. WAVLEN        DD    0        ; (length of .wav file) - 8
  62. WAVESTR        DB    "WAVE"        ; WAVE signature = "WAVE"
  63. FMTSTR        DB    "fmt "        ; format chunk header = "fmt "
  64.         DD    16        ; length of format chunk = 16
  65.         DW    1        ; format type = 1 (Microsoft PCM)
  66. NCHANNELS    DW    0        ; number of channels
  67. SAMPRATE    DD    0        ; samples per second
  68. BYTESPERSEC    DD    0        ; bytes per second 
  69. SAMPSIZE    DW    0        ; bytes per (multichannel) sample
  70. SAMPWIDTH    DW    0        ; bits per channel
  71. DATASTR        DB    "data"        ; data block header = "data"
  72. DATALEN        DD    0        ; number of bytes in data chunk
  73.         ;
  74.         ; Additional output parameters.
  75.         ;
  76. CHANSIZE    DW    0        ; bytes per channel
  77. ISSIGNED    DB    0        ; 1 = output samples are signed
  78. NSAMPLES    DD    0        ; number of samples in output file
  79.         ;
  80.         ; Flag, 1 = format chunk processed.
  81.         ;
  82. FMTDONE        DB    0
  83.         ;
  84.         ; Input file format parameters.
  85.         ;
  86. INISSIGNED    DB    0        ; 1 = input samples are signed
  87. INNCHANNELS    DW    0        ; number of channels
  88. INSAMPRATE    DD    0        ; sampling rate
  89. INSAMPWIDTH    DW    0        ; bits per channel
  90. INCHANSIZE    DW    0        ; bytes per channel
  91. INSAMPSIZE    DW    0        ; bytes per (multichannel) sample
  92. INNSAMPLES    DD    0        ; number of samples in input file
  93.         ;
  94.         ; Small buffer for reading header fields and for processing
  95.         ; samples.
  96.         ;
  97. SMALLBUF    DB    16 DUP (0)
  98.         ;
  99.         ; Small buffers for mixing channels.
  100.         ;
  101. MIXBUF        DB    9 DUP (0)
  102. CHANBUF        DB    9 DUP (0)
  103.         ;
  104.         ; Number of input samples needed to make 1024 output samples.
  105.         ;
  106. SAMPTOREAD    DW    1024        ; change to 2048 if HALVEFLAG on
  107.         ;
  108.         ; Number of bytes to read to get 1024 output samples.
  109.         ;
  110. BYTESTOREAD    DW    0
  111.         ;
  112.         ; Number of bytes from the input buffer that have been
  113.         ; processed.
  114.         ;
  115. BYTESTAKEN    DW    0
  116.         ;
  117.         ; Number of bytes placed in the output buffer.
  118.         ;
  119. BYTESPLACED    DW    0
  120.         ;
  121.         ; Number of input bytes on each channel to skip over.
  122.         ;
  123. SKIPLENGTH    DW    0
  124.         ;
  125.         ; Error messages.
  126.         ;
  127. USAGEMSG    DB    "Usage:  PPWAV <input file> <output file>",0Dh,0Ah
  128.         DB    "- see docs for details.",0Dh,0Ah,"$"
  129. INOPENMSG    DB    "Error opening input file.",0Dh,0Ah,"$"
  130. OUTOPENMSG    DB    "Unable to create output file.",0Dh,0Ah,"$"
  131. READERRMSG    DB    "Error reading input .wav file.",0Dh,0Ah,"$"
  132. WRITEERRMSG    DB    "Error writing output .wav file.",0Dh,0Ah,"$"
  133. BADWAVMSG    DB    "Input .wav file invalid or unsupported type."
  134.         DB    0Dh,0Ah,"$"
  135.         ;
  136.         ; File handles.
  137.         ;
  138. INHANDLE    DW    0        ; input file handle
  139. OUTHANDLE    DW    0        ; output file handle
  140.         ;
  141.         ; Information about the input file to be displayed to the user.
  142.         ;
  143. MSGA        DB    "Input file:  $"
  144. INFILENAME    DB    128 DUP (0)        ; input file name
  145. MSGB        DB    0Dh,0Ah,9,"$"
  146. MSGC        DB    " channels",0Dh,0Ah,9,"$"
  147. MSGD        DB    " samples per second (Hz)",0Dh,0Ah,9,"$"
  148. MSGE        DB    " bits per channel",0Dh,0Ah,9,"$"
  149. MSGF        DB    "samples are signed",0Dh,0Ah,0Dh,0Ah,"$"
  150. MSGG        DB    "samples are unsigned",0Dh,0Ah,0Dh,0Ah,"$"
  151.         ;
  152.         ; Action flags.  1 = action selected.
  153.         ;
  154. MIXFLAG        DB    0
  155. CONVERTFLAG    DB    0
  156. HALVEFLAG    DB    0
  157.         ;
  158.         ; Prompts for the user.
  159.         ;
  160. MONOMSG        DB    "        Mix to mono?$"
  161. CONVERTMSG    DB    "   Convert to 8-bit?$"
  162. HALVEMSG    DB    "Halve sampling rate?$"
  163. YHIMSG        DB    " (Y/n)  $"
  164. NHIMSG        DB    " (y/N)  $"
  165.         ;
  166.         ; Buffer for user input.
  167.         ;
  168. USERBUF        DB    2
  169. ANSWERED    DB    0            ; = 1 if the user answered
  170. ANSWER        DB    2 DUP (0)        ; user's answer in first byte
  171. DATA        ENDS
  172.  
  173. SCODE    SEGMENT
  174. ;
  175. ; Error handling subroutine.  Displays the string addressed by DS:DX and
  176. ; halts the program.
  177. ;
  178. ERROR:
  179.     MOV    AH,9
  180.     INT    21h
  181.     MOV    AX,4C01h        ; return ERRORLEVEL 1
  182.     INT    21h
  183. ;
  184. ; Subroutine, takes pointer to string in DS:SI and length of string in CX,
  185. ; skips over blanks and tabs, returns pointer to first nonblank character
  186. ; in the string in DS:SI, length of remaining string in CX.  If end of
  187. ; string is reached, return pointer to end of string in DS:SI, zero in CX.
  188. ;
  189. SKIPBLANKS:
  190.     PUSH    AX
  191. SKIPBLANKS_LOOP:
  192.     JCXZ    SKIPBLANKS_END
  193.     LODSB
  194.     DEC    CX
  195.     CMP    AL,9
  196.     JE    SKIPBLANKS_LOOP
  197.     CMP    AL,20h
  198.     JE    SKIPBLANKS_LOOP
  199.     DEC    SI
  200.     INC    CX
  201. SKIPBLANKS_END:
  202.     POP    AX
  203.     RET
  204. ;
  205. ; Subroutine, takes pointer to string is DS:SI and length of string in CX,
  206. ; skips over nonblank characters, returns pointer to first blank or tab in
  207. ; the string in DS:SI, length of remaining string in CX.  If end of string
  208. ; is reached, return pointer to end of string in DS:SI, zero in CX.
  209. ;
  210. SKIPNONBLANK:
  211.     PUSH    AX
  212. SKIPNONBLANK_LOOP:
  213.     JCXZ    SKIPNONBLANK_END
  214.     LODSB
  215.     DEC    CX
  216.     CMP    AL,9
  217.     JE    SKIPNONBLANK_LPEND
  218.     CMP    AL,20h
  219.     JNE    SKIPNONBLANK_LOOP
  220. SKIPNONBLANK_LPEND:
  221.     DEC    SI
  222.     INC    CX
  223. SKIPNONBLANK_END:
  224.     POP    AX
  225.     RET
  226. ;
  227. ; Subroutine, reads input and output filenames from the command line, opens
  228. ; the first file, and creates the second file.  Displays a usage message if
  229. ; two filenames are not specified.
  230. ;
  231. OPENFILES:
  232.     PUSH    AX
  233.     PUSH    BX
  234.     PUSH    CX
  235.     PUSH    DX
  236.     PUSH    SI
  237.     PUSH    DI
  238.     ;
  239.     ; Exchange DS, ES.  DS addresses PSP; ES addresses local data.
  240.     ;
  241.     PUSH    DS
  242.     PUSH    ES
  243.     POP    DS
  244.     POP    ES
  245.     ;
  246.     ; Find first command line parameter, save pointer to it in DI.
  247.     ;
  248.     MOV    CL,[80h]
  249.     XOR    CH,CH
  250.     MOV    SI,81h
  251.     CALL    SKIPBLANKS
  252.     JCXZ    OPENFILES_USAGEERR
  253.     MOV    DI,SI
  254.     ;
  255.     ; Find the end of it and save its length in BX.
  256.     ;
  257.     MOV    DX,SI            ; for Int 21h fcn 3Dh
  258.     CALL    SKIPNONBLANK
  259.     JCXZ    OPENFILES_USAGEERR
  260.     MOV    BX,SI
  261.     SUB    BX,DI
  262.     ;
  263.     ; Append a null.
  264.     ;
  265.     MOV    BYTE PTR [SI],0
  266.     INC    SI
  267.     DEC    CX
  268.     JCXZ    OPENFILES_USAGEERR
  269.     ;
  270.     ; Open the input file.
  271.     ;
  272.     MOV    AX,3D20h        ; open read-only, deny writes
  273.     INT    21h
  274.     JC    OPENFILES_OPEN1ERR
  275.     MOV    ES:INHANDLE,AX
  276.     ;
  277.     ; Find the second command line parameter.
  278.     ;
  279.     CALL    SKIPBLANKS
  280.     JCXZ    OPENFILES_USAGEERR
  281.     ;
  282.     ; Append a null.
  283.     ;
  284.     MOV    DX,SI            ; for Int 21h fcn 3Ch
  285.     CALL    SKIPNONBLANK
  286.     MOV    BYTE PTR [SI],0
  287.     ;
  288.     ; Create the output file.
  289.     ;
  290.     MOV    AH,3Ch
  291.     XOR    CX,CX            ; attribute:  normal file
  292.     INT    21h
  293.     JC    OPENFILES_OPEN2ERR
  294.     MOV    ES:OUTHANDLE,AX
  295.     ;
  296.     ; Copy the input filename into the data segment.
  297.     ;
  298.     MOV    SI,DI            ; saved above
  299.     MOV    CX,BX            ; also
  300.     MOV    DI,OFFSET INFILENAME
  301.     REP    MOVSB
  302.     MOV    BYTE PTR ES:[DI],"$"
  303.     ;
  304.     ; Swap DS, ES back.
  305.     ;
  306.     PUSH    DS
  307.     PUSH    ES
  308.     POP    DS
  309.     POP    ES
  310.     ;
  311.     ; Restore registers and exit.
  312.     ;
  313.     POP    DI
  314.     POP    SI
  315.     POP    DX
  316.     POP    CX
  317.     POP    BX
  318.     POP    AX
  319.     RET
  320.     ;
  321.     ; Two files were not specified on the command line.
  322.     ;
  323. OPENFILES_USAGEERR:
  324.     PUSH    ES
  325.     POP    DS
  326.     MOV    DX,OFFSET USAGEMSG
  327.     JMP    ERROR
  328.     ;
  329.     ; Unable to open input file.
  330.     ;
  331. OPENFILES_OPEN1ERR:
  332.     PUSH    ES
  333.     POP    DS
  334.     MOV    DX,OFFSET INOPENMSG
  335.     JMP    ERROR
  336.     ;
  337.     ; Unable to create output file.
  338.     ;
  339. OPENFILES_OPEN2ERR:
  340.     PUSH    ES
  341.     POP    DS
  342.     MOV    DX,OFFSET OUTOPENMSG
  343.     JMP    ERROR
  344. ;
  345. ; Subroutine to read a small number of bytes (specified in CX) from the input
  346. ; file into SMALLBUF.  Returns number of bytes in AX, pointer to SMALLBUF in 
  347. ; DX.
  348. ;
  349. READSMALL:
  350.     PUSH    BX
  351.     MOV    AH,3Fh
  352.     MOV    BX,INHANDLE
  353.     MOV    DX,OFFSET SMALLBUF
  354.     INT    21h
  355.     JC    READSMALL_READERR
  356.     CMP    AX,CX
  357.     JNE    READSMALL_INVALID
  358.     POP    BX
  359.     RET
  360.     ;
  361.     ; Error reading .wav file header.
  362.     ;
  363. READSMALL_READERR:
  364.     MOV    DX,OFFSET READERRMSG
  365.     JMP    ERROR
  366.     ;
  367.     ; Invalid .wav header, or unsupported .wav type.
  368.     ;
  369. READSMALL_INVALID:
  370.     MOV    DX,OFFSET BADWAVMSG
  371.     JMP    ERROR
  372. ;
  373. ; Subroutine for GETWAVHEADER.  Reads 4 bytes from the input file and checks 
  374. ; whether the string read is "fmt ", "data", another ASCII string, or not 
  375. ; ASCII.  Returns AL = 0 if "fmt ", AL = 1 if "data", AL = -1 if another 
  376. ; ASCII string.  Halts the program if not ASCII.  Assumes ES addresses data 
  377. ; segment.  Returns pointer to SMALLBUF in DX.
  378. ;
  379. GETCHUNK:
  380.     PUSH    BX
  381.     PUSH    CX
  382.     PUSH    SI
  383.     PUSH    DI
  384.     ;
  385.     ; Read 4 bytes from the file.
  386.     ;
  387.     MOV    CX,4
  388.     CALL    READSMALL
  389.     ;
  390.     ; Check if format chunk.
  391.     ;
  392.     MOV    SI,DX
  393.     MOV    DI,OFFSET FMTSTR
  394.     REPE    CMPSB
  395.     JNE    GETCHUNK_CHKDATA
  396.     MOV    AL,0
  397.     JMP    GETCHUNK_EXIT
  398.     ;
  399.     ; Check if data chunk.
  400.     ;
  401. GETCHUNK_CHKDATA:
  402.     MOV    SI,DX
  403.     MOV    DI,OFFSET DATASTR
  404.     MOV    CX,4
  405.     REPE    CMPSB
  406.     JNE    GETCHUNK_CHKASCII
  407.     MOV    AL,1
  408.     JMP    GETCHUNK_EXIT
  409.     ;
  410.     ; Check if a valid chunk header of another type.
  411.     ;
  412. GETCHUNK_CHKASCII:
  413.     MOV    SI,DX
  414.     MOV    CX,4
  415. GETCHUNK_ASCLOOP:
  416.     LODSB
  417.     CMP    AL,20h
  418.     JB    GETCHUNK_INVALID
  419.     CMP    AL,7Eh
  420.     JA    GETCHUNK_INVALID
  421.     LOOP    GETCHUNK_ASCLOOP
  422.     MOV    AL,-1
  423.     ;
  424.     ; Read the chunk length field and exit.
  425.     ;
  426. GETCHUNK_EXIT:    PUSH    AX
  427.     MOV    CX,4
  428.     CALL    READSMALL
  429.     POP    AX
  430.     POP    DI
  431.     POP    SI
  432.     POP    CX
  433.     POP    BX
  434.     RET
  435.     ;
  436.     ; Invalid .wav header, or unsupported .wav type.
  437.     ;
  438. GETCHUNK_INVALID:
  439.     MOV    DX,OFFSET BADWAVMSG
  440.     JMP    ERROR
  441. ;
  442. ; Subroutine for GETWAVHEADER.  This routine reads the format chunk from the 
  443. ; input file and records the information found there.  Assumes DS:DX addresses 
  444. ; the chunk length.
  445. ;
  446. DOFORMAT:
  447.     PUSH    AX
  448.     PUSH    BX
  449.     PUSH    CX
  450.     PUSH    DX
  451.     PUSH    SI
  452.     ;
  453.     ; Check chunk length (must be 16).
  454.     ;
  455.     MOV    SI,DX
  456.     LODSW
  457.     CMP    AX,16
  458.     JNE    DOFORMAT_INVALID
  459.     MOV    CX,AX
  460.     LODSW
  461.     OR    AX,AX
  462.     JNZ    DOFORMAT_INVALID
  463.     ;
  464.     ; Read in format chunk.
  465.     ;
  466.     CALL    READSMALL
  467.     ;
  468.     ; Verify format tag.
  469.     ;
  470.     MOV    SI,DX
  471.     LODSW
  472.     CMP    AX,1
  473.     JNE    DOFORMAT_INVALID
  474.     ;
  475.     ; Get number of channels and save.
  476.     ;
  477.     LODSW
  478.     OR    AX,AX            ; invalid if zero channels
  479.     JZ    DOFORMAT_INVALID
  480.     CMP    AX,16            ; an impossibly large value ...
  481.     JA    DOFORMAT_INVALID
  482.     MOV    INNCHANNELS,AX
  483.     ;
  484.     ; Get sampling rate.
  485.     ;
  486.     LODSW
  487.     MOV    WORD PTR INSAMPRATE,AX
  488.     MOV    DX,AX
  489.     LODSW
  490.     OR    DX,AX            ; rate of zero invalid
  491.     OR    DX,DX
  492.     JZ    DOFORMAT_INVALID
  493.     MOV    WORD PTR INSAMPRATE+2,AX
  494.     ;
  495.     ; Get bits per channel.
  496.     ;
  497.     ADD    SI,6            ; skip bytes/sec, block align
  498.     LODSW
  499.     OR    AX,AX            ; zero bits per sample invalid
  500.     JZ    DOFORMAT_INVALID
  501.     CMP    AX,128            ; an impossibly large value ...
  502.     JA    DOFORMAT_INVALID
  503.     MOV    INSAMPWIDTH,AX
  504.     ;
  505.     ; Compute bytes per channel.
  506.     ;
  507.     ADD    AX,7
  508.     SHR    AX,1
  509.     SHR    AX,1
  510.     SHR    AX,1
  511.     MOV    INCHANSIZE,AX
  512.     ;
  513.     ; Compute bytes per (multichannel) sample.
  514.     ;
  515.     MUL    INNCHANNELS
  516.     CMP    AX,16            ; this is what the buffer will hold ...
  517.     JA    DOFORMAT_INVALID
  518.     MOV    INSAMPSIZE,AX
  519.     ;
  520.     ; Are samples signed?
  521.     ;
  522.     CMP    INCHANSIZE,2
  523.     CMC
  524.     RCL    INISSIGNED,1
  525.     ;
  526.     ; Exit.
  527.     ;
  528.     POP    SI
  529.     POP    DX
  530.     POP    CX
  531.     POP    BX
  532.     POP    AX
  533.     RET
  534.     ;
  535.     ; Invalid .wav header, or unsupported .wav type.
  536.     ;
  537. DOFORMAT_INVALID:
  538.     MOV    DX,OFFSET BADWAVMSG
  539.     CALL    ERROR
  540. ;
  541. ; Subroutine for GETWAVHEADER.  This routine skips over an unknown chunk, 
  542. ; updating the file pointer.  Assumes DS:DX addresses the chunk length.
  543. ;
  544. SKIPCHUNK:
  545.     PUSH    AX
  546.     PUSH    BX
  547.     PUSH    CX
  548.     PUSH    DX
  549.     PUSH    SI
  550.     MOV    SI,DX
  551.     LODSW
  552.     MOV    DX,AX
  553.     LODSW
  554.     MOV    CX,AX
  555.     MOV    AX,4201h
  556.     MOV    BX,INHANDLE
  557.     INT    21h
  558.     POP    SI
  559.     POP    DX
  560.     POP    CX
  561.     POP    BX
  562.     POP    AX
  563.     RET
  564. ;
  565. ; Routine to read the .wav header from the input file and compute needed 
  566. ; parameters from it.  Skips unknown chunks.
  567. ;
  568. GETWAVHEADER:
  569.     PUSH    AX
  570.     PUSH    CX
  571.     PUSH    DX
  572.     PUSH    SI
  573.     PUSH    DI
  574.     PUSH    ES
  575.     ;
  576.     ; ES addresses data segment.
  577.     ;
  578.     MOV    AX,DS
  579.     MOV    ES,AX
  580.     ;
  581.     ; Read RIFF and WAVE headers from file.
  582.     ;
  583.     MOV    CX,12
  584.     CALL    READSMALL
  585.     ;
  586.     ; Verify "RIFF".
  587.     ;
  588.     MOV    SI,DX
  589.     MOV    DI,OFFSET RIFFSTR
  590.     MOV    CX,4
  591.     REPE    CMPSB
  592.     JNE    GETWAVHEADER_INVALID
  593.     ;
  594.     ; Verify "WAVE".
  595.     ;
  596.     ADD    SI,4            ; skip length field
  597.     MOV    DI,OFFSET WAVESTR
  598.     MOV    CX,4
  599.     REPE    CMPSB
  600.     JNE    GETWAVHEADER_INVALID
  601.     ;
  602.     ; Loop over chunks until data chunk found.
  603.     ;
  604. GETWAVHEADER_LOOP:
  605.     CALL    GETCHUNK
  606.     CMP    AL,0            ; format chunk?
  607.     JNE    >L0
  608.     CMP    FMTDONE,1        ; error if > 1 format chunk
  609.     JE    GETWAVHEADER_INVALID
  610.     CALL    DOFORMAT
  611.     MOV    FMTDONE,1        ; mark format done
  612.     JMP    GETWAVHEADER_LOOP
  613. L0:    CMP    AL,1            ; data chunk?
  614.     JNE    >L1
  615.     CMP    FMTDONE,0        ; error if format chunk does
  616.     JE    GETWAVHEADER_INVALID    ;   not precede data chunk
  617.     JMP    GETWAVHEADER_LOOPEND
  618. L1:    CALL    SKIPCHUNK        ; unknown chunk, skip
  619.     JMP    GETWAVHEADER_LOOP
  620.     ;
  621.     ; Data chunk found, exit.
  622.     ;
  623. GETWAVHEADER_LOOPEND:
  624.     POP    ES
  625.     POP    DI
  626.     POP    SI
  627.     POP    DX
  628.     POP    CX
  629.     POP    AX
  630.     RET
  631.     ;
  632.     ; Invalid .wav header, or unsupported .wav type.
  633.     ;
  634. GETWAVHEADER_INVALID:
  635.     MOV    DX,OFFSET BADWAVMSG
  636.     CALL    ERROR
  637. ;
  638. ; Subroutine to display a decimal number on the screen.  The number is in
  639. ; DX:AX.
  640. ;
  641. SHOWDECIMAL:
  642.     PUSH    AX
  643.     PUSH    BX
  644.     PUSH    CX
  645.     PUSH    DX
  646.     PUSH    SI
  647.     PUSH    DI
  648.     MOV    BX,10            ; BX = 10 (constant for division)
  649.     XOR    CX,CX            ; CX counts the digits
  650.     MOV    DI,DX            ; DI:SI holds the number while it's
  651.     MOV    SI,AX            ;   being divided away
  652.     MOV    AX,DX
  653.     ;
  654.     ; Divide away the number, saving remainders on the stack.
  655.     ;
  656. SHOWDECIMAL_LOOP1:
  657.     XOR    DX,DX
  658.     DIV    BX
  659.     MOV    DI,AX
  660.     MOV    AX,SI
  661.     DIV    BX
  662.     MOV    SI,AX
  663.     PUSH    DX            ; push digit (in low byte)
  664.     INC    CX            ; bump count
  665.     MOV    AX,DI            ; go again if not zero
  666.     OR    AX,AX
  667.     JNZ    SHOWDECIMAL_LOOP1
  668.     OR    SI,SI
  669.     JNZ    SHOWDECIMAL_LOOP1
  670.     ;
  671.     ; Pop the digits off the stack and display them.  (CX is now the loop
  672.     ; counter.)
  673.     ;
  674. SHOWDECIMAL_LOOP2:
  675.     POP    DX
  676.     ADD    DL,'0'
  677.     MOV    AH,2
  678.     INT    21h
  679.     LOOP    SHOWDECIMAL_LOOP2
  680.     POP    DI
  681.     POP    SI
  682.     POP    DX
  683.     POP    CX
  684.     POP    BX
  685.     POP    AX
  686.     RET
  687. ;
  688. ; Routine to display information about the input .wav for the user.
  689. ;
  690. SHOWWAVINFO:
  691.     PUSH    AX
  692.     PUSH    DX
  693.     ;
  694.     ; Display input filename.
  695.     ;
  696.     MOV    DX,OFFSET MSGA
  697.     MOV    AH,9
  698.     INT    21h
  699.     MOV    DX,OFFSET INFILENAME
  700.     MOV    AH,9
  701.     INT    21h
  702.     MOV    DX,OFFSET MSGB
  703.     MOV    AH,9
  704.     INT    21h
  705.     ;
  706.     ; Display number of channels.
  707.     ;
  708.     MOV    AX,INNCHANNELS
  709.     XOR    DX,DX
  710.     CALL    SHOWDECIMAL
  711.     MOV    DX,OFFSET MSGC
  712.     MOV    AH,9
  713.     INT    21h
  714.     ;
  715.     ; Display sampling rate.
  716.     ;
  717.     MOV    AX,WORD PTR INSAMPRATE
  718.     MOV    DX,WORD PTR INSAMPRATE+2
  719.     CALL    SHOWDECIMAL
  720.     MOV    DX,OFFSET MSGD
  721.     MOV    AH,9
  722.     INT    21h
  723.     ;
  724.     ; Display bits per channel.
  725.     ;
  726.     MOV    AX,INSAMPWIDTH
  727.     XOR    DX,DX
  728.     CALL    SHOWDECIMAL
  729.     MOV    DX,OFFSET MSGE
  730.     MOV    AH,9
  731.     INT    21h
  732.     ;
  733.     ; Display whether samples are signed or unsigned.
  734.     ;
  735.     CMP    INISSIGNED,1
  736.     JNE    >L0
  737.     MOV    DX,OFFSET MSGF
  738.     JMP    >L1
  739. L0:
  740.     MOV    DX,OFFSET MSGG
  741. L1:
  742.     MOV    AH,9
  743.     INT    21h
  744.     POP    DX
  745.     POP    AX
  746.     RET
  747. ;
  748. ; Subroutine to display a prompt to the user and get a "yes" or "no" answer.
  749. ; Takes 3 parameters.  DS:DX addresses the prompt to be displayed (terminated
  750. ; with a dollar sign).  DS:BX addresses the location where the answer will
  751. ; be stored (0 = no, 1 = yes).  AL is the default answer (if the user just
  752. ; hits return or enters something other than "y" or "n").
  753. ;
  754. GETYN:
  755.     PUSH    AX
  756.     PUSH    CX
  757.     PUSH    DX
  758.     ;
  759.     ; Save default answer in CL.
  760.     ;
  761.     MOV    CL,AL
  762.     ;
  763.     ; Display the prompt.
  764.     ;
  765.     MOV    AH,9
  766.     INT    21h
  767.     ;
  768.     ; Display "(Y/n)" if "yes" is the default, or "(y/N)" if "no" is
  769.     ; the default.
  770.     ;
  771.     CMP    CL,1
  772.     JNE    >L0
  773.     MOV    DX,OFFSET YHIMSG
  774.     JMP    >L1
  775. L0:
  776.     MOV    DX,OFFSET NHIMSG
  777. L1:
  778.     MOV    AH,9
  779.     INT    21h
  780.     ;
  781.     ; Get the answer.
  782.     ;
  783.     MOV    AH,0Ah
  784.     MOV    DX,OFFSET USERBUF
  785.     INT    21h
  786.     ;
  787.     ; Set result according to answer.
  788.     ;
  789.     MOV    [BX],CL                ; assume no (default) answer
  790.     CMP    BYTE PTR ANSWERED,1        ; just exit if no answer
  791.     JNE    GETYN_EXIT
  792.     ;
  793.     ; Get answer in AL and convert to 0 or 1.
  794.     ;
  795.     MOV    AL,ANSWER
  796.     AND    AL,0DFh                ; convert to uppercase
  797.     CMP    AL,'Y'
  798.     JNE    >L0
  799.     MOV    AL,1
  800.     JMP    >L1
  801. L0:
  802.     CMP    AL,'N'
  803.     JNE    GETYN_EXIT
  804.     MOV    AL,0
  805. L1:
  806.     MOV    [BX],AL
  807.     ;
  808.     ; Move cursor to next line.
  809.     ;
  810. GETYN_EXIT:
  811.     MOV    AH,2
  812.     MOV    DL,0Dh
  813.     INT    21h
  814.     MOV    AH,2
  815.     MOV    DL,0Ah
  816.     INT    21h
  817.     POP    DX
  818.     POP    CX
  819.     POP    AX
  820.     RET
  821. ;
  822. ; Routine to prompt the user for the desired actions.  The user may choose
  823. ; to do one or more of the following:  mix to mono, convert to 8-bit, and/or
  824. ; cut the sampling rate in half.
  825. ;
  826. GETACTIONS:
  827.     PUSH    AX
  828.     PUSH    BX
  829.     PUSH    DX
  830.     ;
  831.     ; Ask if the user wants to mix to mono.
  832.     ;
  833.     MOV    DX,OFFSET MONOMSG
  834.     MOV    BX,OFFSET MIXFLAG
  835.     MOV    AL,1
  836.     CALL    GETYN
  837.     ;
  838.     ; Ask if the user wants to convert to 8-bit.
  839.     ;
  840.     MOV    DX,OFFSET CONVERTMSG
  841.     MOV    BX,OFFSET CONVERTFLAG
  842.     MOV    AL,1
  843.     CALL    GETYN
  844.     ;
  845.     ; Ask if the user wants to cut the sampling rate in half.
  846.     ;
  847.     MOV    DX,OFFSET HALVEMSG
  848.     MOV    BX,OFFSET HALVEFLAG
  849.     MOV    AL,0
  850.     CALL    GETYN
  851.     POP    DX
  852.     POP    BX
  853.     POP    AX
  854.     RET
  855. ;
  856. ; Routine to fill in the fields in the output .wav header and write the
  857. ; header to the output file.
  858. ;
  859. MAKEHEADER:
  860.     PUSH    AX
  861.     PUSH    BX
  862.     PUSH    CX
  863.     PUSH    DX
  864.     PUSH    SI
  865.     PUSH    DI
  866.     ;
  867.     ; Determine number of channels in the output file.
  868.     ;
  869.     CMP    MIXFLAG,0
  870.     JNE    >L0
  871.     MOV    AX,INNCHANNELS            ; no mixing
  872.     MOV    NCHANNELS,AX
  873.     JMP    >L1
  874. L0:
  875.     MOV    NCHANNELS,1            ; mix to mono
  876.     ;
  877.     ; Determine sampling rate for output file.
  878.     ;
  879. L1:
  880.     MOV    AX,WORD PTR INSAMPRATE
  881.     MOV    DX,WORD PTR INSAMPRATE+2
  882.     CMP    HALVEFLAG,0
  883.     JE    >L2
  884.     SHR    DX,1                ; cut sampling rate in half
  885.     RCR    AX,1
  886. L2:
  887.     MOV    WORD PTR SAMPRATE,AX
  888.     MOV    WORD PTR SAMPRATE+2,DX
  889.     ;
  890.     ; Determine number of bits per channel for the output file.
  891.     ;
  892.     CMP    CONVERTFLAG,0
  893.     JNE    >L3
  894.     MOV    AX,INSAMPWIDTH            ; do not convert to 8-bit
  895.     MOV    SAMPWIDTH,AX
  896.     MOV    AX,INCHANSIZE
  897.     MOV    CHANSIZE,AX
  898.     JMP    >L4
  899. L3:
  900.     MOV    SAMPWIDTH,8            ; convert to 8-bit
  901.     MOV    AX,1
  902.     MOV    CHANSIZE,AX
  903.     ;
  904.     ; Determine whether output samples are signed.
  905.     ;
  906. L4:
  907.     CMP    AX,2
  908.     CMC
  909.     RCL    ISSIGNED,1
  910.     ;
  911.     ; Determine number of bytes per (multichannel) sample.
  912.     ;
  913.     MUL    NCHANNELS
  914.     MOV    SAMPSIZE,AX
  915.     ;
  916.     ; Determine number of bytes per second.
  917.     ;
  918.     MOV    AX,WORD PTR SAMPRATE
  919.     MUL    SAMPSIZE
  920.     MOV    WORD PTR BYTESPERSEC,AX
  921.     MOV    BX,DX
  922.     MOV    AX,WORD PTR SAMPRATE+2
  923.     MUL    SAMPSIZE
  924.     ADD    AX,BX
  925.     MOV    WORD PTR BYTESPERSEC+2,AX
  926.     ;
  927.     ; Determine number of (multichannel) samples from length of input
  928.     ; file.
  929.     ;
  930.     MOV    AX,4201h        ; get current input file pointer
  931.     MOV    BX,INHANDLE
  932.     XOR    CX,CX
  933.     XOR    DX,DX
  934.     INT    21h
  935.     JNC    >L5
  936.     JMP    MAKEHEADER_READERR
  937. L5:
  938.     MOV    DI,DX            ; save in DI:SI
  939.     MOV    SI,AX
  940.     MOV    AX,4202h        ; seek to end of input file
  941.     MOV    BX,INHANDLE
  942.     XOR    CX,CX
  943.     XOR    DX,DX
  944.     INT    21h
  945.     JNC    >L6
  946.     JMP    MAKEHEADER_READERR
  947. L6:
  948.     SUB    AX,SI            ; determine number of sample bytes
  949.     SBB    DX,DI
  950.     MOV    BX,AX            ; divide high word by length of sample
  951.     MOV    AX,DX
  952.     XOR    DX,DX
  953.     DIV    INSAMPSIZE
  954.     MOV    WORD PTR INNSAMPLES+2,AX    ; save high word of result
  955.     MOV    AX,BX                ; divide low word + remainder
  956.     DIV    INSAMPSIZE
  957.     MOV    WORD PTR INNSAMPLES,AX    ; save low word of result
  958.     MOV    AX,4200h        ; seek back to start of sample data
  959.     MOV    BX,INHANDLE
  960.     MOV    CX,DI
  961.     MOV    DX,SI
  962.     INT    21h
  963.     JC    MAKEHEADER_READERR
  964.     ;
  965.     ; Determine number of output samples.
  966.     ;
  967.     MOV    AX,WORD PTR INNSAMPLES
  968.     MOV    DX,WORD PTR INNSAMPLES+2
  969.     CMP    HALVEFLAG,0
  970.     JE    >L7
  971.     ADD    AX,1            ; divide by 2 and round up
  972.     ADC    DX,0
  973.     SHR    DX,1
  974.     RCR    AX,1
  975. L7:
  976.     MOV    WORD PTR NSAMPLES,AX
  977.     MOV    WORD PTR NSAMPLES+2,DX
  978.     ;
  979.     ; Determine length of output data.
  980.     ;
  981.     MUL    SAMPSIZE
  982.     MOV    WORD PTR DATALEN,AX
  983.     MOV    BX,DX
  984.     MOV    AX,WORD PTR NSAMPLES+2
  985.     MUL    SAMPSIZE
  986.     ADD    AX,BX
  987.     MOV    WORD PTR DATALEN+2,AX
  988.     ;
  989.     ; Determine length of output .wav file.
  990.     ;
  991.     MOV    DX,AX
  992.     MOV    AX,WORD PTR DATALEN
  993.     ADD    AX,36
  994.     ADC    DX,0
  995.     MOV    WORD PTR WAVLEN,AX
  996.     MOV    WORD PTR WAVLEN+2,DX
  997.     ;
  998.     ; Write the header out.
  999.     ;
  1000.     MOV    AH,40h
  1001.     MOV    BX,OUTHANDLE
  1002.     MOV    CX,44
  1003.     MOV    DX,WAVHEADER
  1004.     INT    21h
  1005.     JC    MAKEHEADER_WRITEERR
  1006.     POP    DI
  1007.     POP    SI
  1008.     POP    DX
  1009.     POP    CX
  1010.     POP    BX
  1011.     POP    AX
  1012.     RET
  1013.     ;
  1014.     ; Seek error on input file.
  1015.     ;
  1016. MAKEHEADER_READERR:
  1017.     MOV    DX,OFFSET READERRMSG
  1018.     JMP    ERROR
  1019.     ;
  1020.     ; Error writing to output file.
  1021.     ;
  1022. MAKEHEADER_WRITEERR:
  1023.     MOV    DX,OFFSET WRITEERRMSG
  1024.     JMP    ERROR
  1025. ;
  1026. ; Subroutine, reads data into input buffer, sufficient to make 1024 output
  1027. ; samples.  The amount of data successfully read is returned in CX - the
  1028. ; equivalent in output samples of the data successfully read.  Halts the
  1029. ; program with an error message on file error.
  1030. ;
  1031. READDATA:
  1032.     PUSH    AX
  1033.     PUSH    BX
  1034.     PUSH    DX
  1035.     ;
  1036.     ; Read from the input file.
  1037.     ;
  1038.     PUSH    DS
  1039.     MOV    AH,3Fh
  1040.     MOV    BX,INHANDLE
  1041.     MOV    CX,BYTESTOREAD
  1042.     MOV    DX,SEG INBUF
  1043.     MOV    DS,DX
  1044.     XOR    DX,DX
  1045.     INT    21h
  1046.     POP    DS
  1047.     JC    READDATA_READERR
  1048.     ;
  1049.     ; Check if the full amount requested was read.
  1050.     ;
  1051.     CMP    AX,CX
  1052.     JB    READDATA_EOF
  1053.     ;
  1054.     ; Got the full amount.
  1055.     ;
  1056.     MOV    CX,1024
  1057.     JMP    READDATA_EXIT
  1058.     ;
  1059.     ; End of file - less than the full amount requested was read.
  1060.     ;
  1061. READDATA_EOF:
  1062.     XOR    DX,DX            ; calculate number of samples read
  1063.     DIV    INSAMPSIZE
  1064.     MOV    CX,AX
  1065.     CMP    HALVEFLAG,0        ; if not halving, exit
  1066.     JE    READDATA_EXIT
  1067.     INC    CX            ; halving - divide by 2 and round up
  1068.     SHR    CX,1
  1069.     ;
  1070.     ; Exit.
  1071.     ;
  1072. READDATA_EXIT:
  1073.     POP    DX
  1074.     POP    BX
  1075.     POP    AX
  1076.     RET
  1077.     ;
  1078.     ; Error reading input file.
  1079.     ;
  1080. READDATA_READERR:
  1081.     MOV    DX,OFFSET READERRMSG
  1082.     JMP    ERROR
  1083. ;
  1084. ; Subroutine to write out the output buffer contents.  Halts the program
  1085. ; with an error message if unsuccessful.  Uses BYTESPLACED.
  1086. ;
  1087. WRITEDATA:
  1088.     PUSH    AX
  1089.     PUSH    BX
  1090.     PUSH    CX
  1091.     PUSH    DX
  1092.     PUSH    DS
  1093.     MOV    AH,40h
  1094.     MOV    BX,OUTHANDLE
  1095.     MOV    CX,BYTESPLACED
  1096.     MOV    DX,SEG OUTBUF
  1097.     MOV    DS,DX
  1098.     XOR    DX,DX
  1099.     INT    21h
  1100.     POP    DS
  1101.     JC    WRITEDATA_WRITEERR
  1102.     POP    DX
  1103.     POP    CX
  1104.     POP    BX
  1105.     POP    AX
  1106.     RET
  1107.     ;    
  1108.     ; Error writing to output file.
  1109.     ;
  1110. WRITEDATA_WRITEERR:
  1111.     MOV    DX,OFFSET WRITEERRMSG
  1112.     JMP    ERROR
  1113. ;
  1114. ; Subroutine to copy a multichannel sample from the input buffer to the small 
  1115. ; buffer in the data segment.
  1116. ;
  1117. COPYSAMPLE:
  1118.     PUSH    AX
  1119.     PUSH    CX
  1120.     PUSH    SI
  1121.     PUSH    DI
  1122.     PUSH    DS
  1123.     PUSH    ES
  1124.     MOV    CX,INSAMPSIZE            ; CX = number of bytes to copy
  1125.     MOV    SI,BYTESTAKEN            ; DS:SI -> bytes to copy
  1126.     PUSH    DS
  1127.     POP    ES                ; ES:DI -> small buffer
  1128.     MOV    AX,SEG INBUF
  1129.     MOV    DS,AX
  1130.     MOV    DI,OFFSET SMALLBUF
  1131.     REP    MOVSB
  1132.     POP    ES
  1133.     POP    DS
  1134.     MOV    BYTESTAKEN,SI
  1135.     POP    DI
  1136.     POP    SI
  1137.     POP    CX
  1138.     POP    AX
  1139.     RET
  1140. ;
  1141. ; Subroutine to convert the multichannel sample in the small buffer to 8-bit.
  1142. ;
  1143. CONVTO8:
  1144.     CMP    INCHANSIZE,1        ; if already 8-bit, return immediately
  1145.     JNE    CONVTO8_PROCEED
  1146.     RET
  1147. CONVTO8_PROCEED:
  1148.     PUSH    AX
  1149.     PUSH    CX
  1150.     PUSH    SI
  1151.     PUSH    DI
  1152.     PUSH    ES
  1153.     MOV    SI,OFFSET SMALLBUF
  1154.     PUSH    DS
  1155.     POP    ES
  1156.     MOV    DI,SI
  1157.     MOV    CX,INNCHANNELS
  1158. CONVTO8_CHANLOOP:
  1159.     ADD    SI,SKIPLENGTH
  1160.     LODSB
  1161.     ADD    AL,128
  1162.     STOSB
  1163.     LOOP    CONVTO8_CHANLOOP
  1164.     POP    ES
  1165.     POP    DI
  1166.     POP    SI
  1167.     POP    CX
  1168.     POP    AX
  1169.     RET
  1170. ;
  1171. ; Subroutine to mix a multichannel 8-bit unsigned sample.  The sample is
  1172. ; assumed to be in the small buffer, and ES and DS are assumed to address
  1173. ; the data segment.  The result is placed back in the small buffer.
  1174. ;
  1175. MIX8:
  1176.     PUSH    AX
  1177.     PUSH    BX
  1178.     PUSH    CX
  1179.     PUSH    DX
  1180.     PUSH    SI
  1181.     ;
  1182.     ; DS:SI addresses the sample.
  1183.     ;
  1184.     MOV    SI,OFFSET SMALLBUF
  1185.     ;
  1186.     ; Add up the channels.
  1187.     ;
  1188.     MOV    CX,INNCHANNELS
  1189.     XOR    BX,BX
  1190. MIX8_ADDLOOP:
  1191.     LODSB
  1192.     ADD    BL,AL
  1193.     ADC    BH,0
  1194.     LOOP    MIX8_ADDLOOP
  1195.     ;
  1196.     ; Divide by the number of input channels and save result.
  1197.     ;
  1198.     MOV    AX,BX
  1199.     XOR    DX,DX
  1200.     DIV    INNCHANNELS
  1201.     MOV    SMALLBUF,AL
  1202.     POP    SI
  1203.     POP    DX
  1204.     POP    CX
  1205.     POP    BX
  1206.     POP    AX
  1207.     RET
  1208. ;
  1209. ; Subroutine to mix a multichannel 16 (or more)-bit signed sample.  The
  1210. ; sample is assumed to be in the small buffer, and ES and DS are assumed
  1211. ; to address the data segment.  The result is placed back in the small
  1212. ; buffer.
  1213. ;
  1214. MIX16:
  1215.     PUSH    AX
  1216.     PUSH    BX
  1217.     PUSH    CX
  1218.     PUSH    DX
  1219.     PUSH    SI
  1220.     PUSH    DI
  1221.     ;
  1222.     ; Clear the mixing buffer.
  1223.     ;
  1224.     MOV    DI,OFFSET MIXBUF
  1225.     MOV    AL,0
  1226.     MOV    CX,CHANSIZE
  1227.     REP    STOSB
  1228.     STOSB
  1229.     ;
  1230.     ; Loop over the channels.
  1231.     ;
  1232.     MOV    SI,OFFSET SMALLBUF
  1233.     MOV    CX,INNCHANNELS
  1234. MIX16_ADDLOOP:
  1235.     PUSH    CX
  1236.     ;
  1237.     ; Copy all but the last byte of the channel to the channel buffer.
  1238.     ;
  1239.     MOV    CX,CHANSIZE        ; CHANSIZE is at least 2 for signed
  1240.     DEC    CX
  1241.     MOV    DI,OFFSET CHANBUF
  1242.     REP    MOVSB
  1243.     ;
  1244.     ; Get the last byte, sign extend, add to convert to unsigned, and
  1245.     ; place in the channel buffer.
  1246.     ;
  1247.     LODSB
  1248.     CBW
  1249.     ADD    AX,80h
  1250.     MOV    [DI],AX
  1251.     ;
  1252.     ; Add the channel to the mixing buffer.
  1253.     ;
  1254.     PUSH    SI
  1255.     MOV    SI,OFFSET CHANBUF
  1256.     MOV    DI,OFFSET MIXBUF
  1257.     MOV    CX,CHANSIZE
  1258.     INC    CX
  1259.     CLC
  1260.     LAHF
  1261. MIX16_DOADD:
  1262.     LODSB
  1263.     SAHF
  1264.     ADC    [DI],AL
  1265.     LAHF
  1266.     INC    DI
  1267.     LOOP    MIX16_DOADD
  1268.     POP    SI
  1269.     ;
  1270.     ; Go to next channel.
  1271.     ;
  1272.     POP    CX
  1273.     LOOP    MIX16_ADDLOOP
  1274.     ;
  1275.     ; Divide by the number of channels.
  1276.     ;
  1277.     STD                ; set direction to decrement
  1278.     MOV    CX,CHANSIZE
  1279.     MOV    SI,CX
  1280.     ADD    SI,OFFSET MIXBUF
  1281.     DEC    CX
  1282.     MOV    DI,CX
  1283.     ADD    DI,OFFSET SMALLBUF
  1284.     ;
  1285.     ; Divide high 2 bytes as word to avoid overflow.
  1286.     ;
  1287.     LODSB
  1288.     MOV    AH,AL
  1289.     LODSB
  1290.     XOR    DX,DX
  1291.     DIV    INNCHANNELS
  1292.     STOSB                ; discard high 8 bits of quotient
  1293.     MOV    AH,DL
  1294.     ;
  1295.     ; Complete the division.
  1296.     ;
  1297.     MOV    BL,BYTE PTR INNCHANNELS
  1298. MIX16_DIVLOOP:
  1299.     LODSB
  1300.     DIV    BL
  1301.     STOSB
  1302.     LOOP    MIX16_DIVLOOP
  1303.     CLD                ; restore direction flag
  1304.     ;
  1305.     ; Convert the result to signed.
  1306.     ;
  1307.     MOV    SI,OFFSET SMALLBUF-1
  1308.     ADD    SI,CHANSIZE
  1309.     ADD    BYTE PTR [SI],80h
  1310.     POP    DI
  1311.     POP    SI
  1312.     POP    DX
  1313.     POP    CX
  1314.     POP    BX
  1315.     POP    AX
  1316.     RET
  1317. ;
  1318. ; Subroutine to mix the multichannel sample in the small buffer to mono.
  1319. ; Assumes that the sample has already been converted to 8-bit if that was
  1320. ; to be done.  This version special-cases 8-bit samples.
  1321. ;
  1322. ; Exit immediately if already mono.
  1323. ;
  1324. DOMIX:
  1325.     CMP    INNCHANNELS,1
  1326.     JNE    DOMIX_PROCEED
  1327.     RET
  1328.     ;
  1329.     ; Both DS and ES address the data segment.
  1330.     ;
  1331. DOMIX_PROCEED:
  1332.     PUSH    ES
  1333.     PUSH    DS
  1334.     POP    ES
  1335.     ;
  1336.     ; Call the appropriate subroutine for signed or unsigned (8-bit)
  1337.     ; samples.
  1338.     ;
  1339.     CMP    ISSIGNED,0
  1340.     JE    DOMIX_8BIT
  1341.     CALL    MIX16
  1342.     JMP    DOMIX_EXIT
  1343. DOMIX_8BIT:
  1344.     CALL    MIX8
  1345. DOMIX_EXIT:
  1346.     POP    ES
  1347.     RET
  1348. ;
  1349. ; Subroutine to copy a sample from the small buffer to the output buffer.
  1350. ; Uses and updates BYTESPLACED.
  1351. ;
  1352. PUTSAMPLE:
  1353.     PUSH    CX
  1354.     PUSH    SI
  1355.     PUSH    DI
  1356.     PUSH    ES
  1357.     MOV    CX,SEG OUTBUF
  1358.     MOV    ES,CX
  1359.     MOV    DI,BYTESPLACED
  1360.     MOV    SI,OFFSET SMALLBUF
  1361.     MOV    CX,SAMPSIZE
  1362.     REP    MOVSB
  1363.     MOV    BYTESPLACED,DI
  1364.     POP    ES
  1365.     POP    DI
  1366.     POP    SI
  1367.     POP    CX
  1368.     RET
  1369. ;
  1370. ; Routine to perform the actual conversion of samples in the input file and
  1371. ; write the results to the output file.  Samples are converted 1024 output
  1372. ; samples at a time; if HALVEFLAG is on, this will be 2048 input samples.
  1373. ;
  1374. PERFORM:
  1375.     PUSH    AX
  1376.     ;
  1377.     ; Determine number of input samples for each pass.
  1378.     ;
  1379.     CMP    HALVEFLAG,0
  1380.     JE    PERFORM_1024
  1381.     MOV    SAMPTOREAD,2048
  1382. PERFORM_1024:
  1383.     ;
  1384.     ; Determine number of input bytes for each pass.
  1385.     ;
  1386.     MOV    AX,SAMPTOREAD
  1387.     MUL    INSAMPSIZE
  1388.     MOV    BYTESTOREAD,AX
  1389.     ;
  1390.     ; Determine number of bytes to "skip over" from each input channel.
  1391.     ;
  1392.     MOV    AX,INCHANSIZE
  1393.     SUB    AX,CHANSIZE
  1394.     MOV    SKIPLENGTH,AX
  1395.     ;
  1396.     ; Main loop.  Read data (BYTESTOREAD bytes) from the input file into
  1397.     ; the input buffer.
  1398.     ;
  1399. PERFORM_MAINLOOP:
  1400.     CALL    READDATA
  1401.     JCXZ    PERFORM_MAINLPEND        ; exit loop and end of file
  1402.     ;
  1403.     ; Number of bytes taken from the input buffer set to zero.
  1404.     ;
  1405.     MOV    BYTESTAKEN,0
  1406.     ;
  1407.     ; Number of bytes placed in output buffer set to zero.
  1408.     ;
  1409.     MOV    BYTESPLACED,0
  1410.     ;
  1411.     ; Loop over samples in the buffer.
  1412.     ;
  1413. PERFORM_SAMPLOOP:
  1414.     CALL    COPYSAMPLE        ; get a sample in the small buffer
  1415.     ;
  1416.     ; If converting to 8-bit, do the conversion.
  1417.     ;
  1418.     CMP    CONVERTFLAG,0
  1419.     JE    PERFORM_CHKMIX
  1420.     CALL    CONVTO8
  1421.     ;
  1422.     ; If mixing to mono, do so.
  1423.     ;
  1424. PERFORM_CHKMIX:
  1425.     CMP    MIXFLAG,0
  1426.     JE    PERFORM_DOPUT
  1427.     CALL    DOMIX
  1428.     ;
  1429.     ; Put the sample in the output buffer.
  1430.     ;
  1431. PERFORM_DOPUT:
  1432.     CALL    PUTSAMPLE
  1433.     ;
  1434.     ; If halving the sampling rate, skip a sample.
  1435.     ;
  1436.     CMP    HALVEFLAG,0
  1437.     JE    PERFORM_SAMPLPTEST
  1438.     MOV    AX,BYTESTAKEN
  1439.     ADD    AX,INSAMPSIZE
  1440.     MOV    BYTESTAKEN,AX
  1441. PERFORM_SAMPLPTEST:
  1442.     LOOP    PERFORM_SAMPLOOP
  1443.     ;
  1444.     ; Write the output buffer and go read more data.
  1445.     ;
  1446.     CALL    WRITEDATA
  1447.     JMP    PERFORM_MAINLOOP
  1448. PERFORM_MAINLPEND:
  1449.     POP    AX
  1450.     RET
  1451. ;
  1452. ; Main program.
  1453. ;
  1454. MAIN:
  1455.     ;
  1456.     ; DS addresses data segment (always - if any routine changes DS, it
  1457.     ; must change it back before returning).
  1458.     ;
  1459.     MOV    AX,SEG SDATA
  1460.     MOV    DS,AX
  1461.     ;
  1462.     ; Set direction to increment (always - if any routine changes DF, it
  1463.     ; must restore it before returning).
  1464.     ;
  1465.     CLD
  1466.     ;
  1467.     ; Open input file and create output file.
  1468.     ;
  1469.     CALL    OPENFILES
  1470.     ;
  1471.     ; Read the .wav header from the input file.
  1472.     ;
  1473.     CALL    GETWAVHEADER
  1474.     ;
  1475.     ; Display the header information for the user.
  1476.     ;
  1477.     CALL    SHOWWAVINFO
  1478.     ;
  1479.     ; Get desired actions from user.
  1480.     ;
  1481.     CALL    GETACTIONS
  1482.     ;
  1483.     ; Fill in fields in the output .wav header and write it out.
  1484.     ;
  1485.     CALL    MAKEHEADER
  1486.     ;
  1487.     ; Convert the file.
  1488.     ;
  1489.     CALL    PERFORM
  1490.     ;
  1491.     ; Terminate.
  1492.     ;
  1493.     MOV    AX,4C00h
  1494.     INT    21h
  1495. SCODE    ENDS
  1496.     END    MAIN
  1497.